// InstDecode.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include "yemu.h"

int64_t SignExt(uint64_t data, int sign_bit) {
    int64_t mask = 1LL << (sign_bit - 1);
    return (data ^ mask) - mask;
}

// I-type immediate: bits [31:20], sign-extended to 64 bits
uint64_t ImmI(uint32_t inst) {
    uint32_t imm = (inst >> 20) & 0xFFF; // bits [31:20]
    return SignExt(imm, 12);
}

// S-type immediate: bits [31:25] and [11:7], sign-extended to 64 bits
uint64_t ImmS(uint32_t inst) {
    uint32_t imm_11_5 = (inst >> 25) & 0x7F; // bits [31:25]
    uint32_t imm_4_0  = (inst >> 7) & 0x1F;  // bits [11:7]
    uint32_t imm = (imm_11_5 << 5) | imm_4_0;
    return SignExt(imm, 12);
}

// B-type immediate: [31] [7] [30:25] [11:8] (with bit 0 always zero), sign-extended to 64 bits
uint64_t ImmB(uint32_t inst) {
    uint32_t imm_12   = (inst >> 31) & 0x1;   // bit [31] -> imm[12]
    uint32_t imm_10_5 = (inst >> 25) & 0x3F;  // bits [30:25] -> imm[10:5]
    uint32_t imm_4_1  = (inst >> 8)  & 0xF;   // bits [11:8] -> imm[4:1]
    uint32_t imm_11   = (inst >> 7)  & 0x1;   // bit [7] -> imm[11]
    uint32_t imm = (imm_12 << 12) | (imm_11 << 11) | (imm_10_5 << 5) | (imm_4_1 << 1);
    return SignExt(imm, 13);
}

// U-type immediate: bits [31:12], lower 12 bits are zero, , sign-extended to 64 bits
uint64_t ImmU(uint32_t inst) {
    uint32_t imm = inst & 0xFFFFF000ULL; // bits [31:12] << 12】
    return SignExt(imm, 32);
}

// J-type immediate: [31] [19:12] [20] [30:21] (with bit 0 always zero), sign-extended to 64 bits
uint64_t ImmJ(uint32_t inst) {
    uint32_t imm_20    = (inst >> 31) & 0x1;    // bit [31] -> imm[20]
    uint32_t imm_10_1  = (inst >> 21) & 0x3FF;  // bits [30:21] -> imm[10:1]
    uint32_t imm_11    = (inst >> 20) & 0x1;    // bit [20] -> imm[11]
    uint32_t imm_19_12 = (inst >> 12) & 0xFF;   // bits [19:12] -> imm[19:12]
    uint32_t imm = (imm_20 << 20) | (imm_19_12 << 12) | (imm_11 << 11) | (imm_10_1 << 1);
    return SignExt(imm, 21);
}

uint32_t RD(uint32_t Inst) {  return (Inst >> 7) & 0x1f; }
uint32_t RS1(uint32_t Inst) { return (Inst >> 15) & 0x1f; }
uint32_t RS2(uint32_t Inst) { return (Inst >> 20) & 0x1f; }
uint64_t IMM(struct INSTINFO InstInfo) {
    switch(InstInfo.InstKind) {
        case ADDI: case SLTI: case SLTIU: case XORI: case ORI: case ANDI:
        case LB: case LH: case LW: case LD: case LBU: case LHU: case LWU:
        case ADDIW: case SLLI: case SRLI: case SRAI:
        case SLLIW: case SRLIW: case SRAIW:
        case JALR:
            return ImmI(InstInfo.Inst);
        case SB: case SH: case SW: case SD:
            return ImmS(InstInfo.Inst);
        case BEQ: case BNE: case BLT: case BGE: case BLTU: case BGEU:
            return ImmB(InstInfo.Inst);
        case LUI: case AUIPC:
            return ImmU(InstInfo.Inst);
        case JAL:
            return ImmJ(InstInfo.Inst);
        default:
            return 0;
    }
}

int BitPat(uint32_t inst, const char* pattern)
{
    char pat[33];
    int patlen = 0;
    for (const char* p = pattern; *p; p++) {
        if (*p == '_') continue;
        if (*p != '0' && *p != '1' && *p != '?') assert(0);
        if (patlen >= 32) assert(0); // too long
        pat[patlen++] = *p;
    }
    if (patlen != 32) assert(0);

    for (int i = 0; i < 32; i++) {
        char c = pat[i];
        if (c == '?') continue;
        int bit = (inst >> (31 - i)) & 1;
        if (bit != (c - '0')) return 0;
    }
    return 1;
}

struct INSTINFO InstDecode(uint32_t Inst) {
    
    struct INSTINFO InstInfo;
    InstInfo.Inst = Inst;
    InstInfo.InstKind = ILL;
    InstInfo.rd = RD(Inst);
    InstInfo.rs1 = RS1(Inst);
    InstInfo.rs2 = RS2(Inst);
    InstInfo.imm = 0;

    // Instruction Formats: I
    if(BitPat(Inst, "000000??????_?????_001_?????_0010011")) InstInfo.InstKind = SLLI;
    if(BitPat(Inst, "000000??????_?????_001_?????_0011011")) InstInfo.InstKind = SLLIW;
	if(BitPat(Inst, "000000??????_?????_101_?????_0010011")) InstInfo.InstKind = SRLI;
	if(BitPat(Inst, "000000??????_?????_101_?????_0011011")) InstInfo.InstKind = SRLIW;
	if(BitPat(Inst, "010000??????_?????_101_?????_0010011")) InstInfo.InstKind = SRAI;
	if(BitPat(Inst, "010000??????_?????_101_?????_0011011")) InstInfo.InstKind = SRAIW;
	if(BitPat(Inst, "????????????_?????_000_?????_0010011")) InstInfo.InstKind = ADDI;
	if(BitPat(Inst, "????????????_?????_000_?????_0011011")) InstInfo.InstKind = ADDIW;
	if(BitPat(Inst, "????????????_?????_100_?????_0010011")) InstInfo.InstKind = XORI;
	if(BitPat(Inst, "????????????_?????_110_?????_0010011")) InstInfo.InstKind = ORI;
	if(BitPat(Inst, "????????????_?????_111_?????_0010011")) InstInfo.InstKind = ANDI;
	if(BitPat(Inst, "????????????_?????_010_?????_0010011")) InstInfo.InstKind = SLTI;
	if(BitPat(Inst, "????????????_?????_011_?????_0010011")) InstInfo.InstKind = SLTIU;
	if(BitPat(Inst, "????????????_?????_000_?????_1100111")) InstInfo.InstKind = JALR;
	if(BitPat(Inst, "????????????_?????_011_?????_0000011")) InstInfo.InstKind = LD;
	if(BitPat(Inst, "????????????_?????_010_?????_0000011")) InstInfo.InstKind = LW;
	if(BitPat(Inst, "????????????_?????_110_?????_0000011")) InstInfo.InstKind = LWU;
	if(BitPat(Inst, "????????????_?????_001_?????_0000011")) InstInfo.InstKind = LH;
	if(BitPat(Inst, "????????????_?????_101_?????_0000011")) InstInfo.InstKind = LHU;
	if(BitPat(Inst, "????????????_?????_000_?????_0000011")) InstInfo.InstKind = LB;
	if(BitPat(Inst, "????????????_?????_100_?????_0000011")) InstInfo.InstKind = LBU;
	// Instruction Formats: R
	if(BitPat(Inst, "000000??????_?????_001_?????_0110011")) InstInfo.InstKind = SLL;
	if(BitPat(Inst, "000000??????_?????_001_?????_0111011")) InstInfo.InstKind = SLLW;
	if(BitPat(Inst, "000000??????_?????_101_?????_0110011")) InstInfo.InstKind = SRL;
	if(BitPat(Inst, "000000??????_?????_101_?????_0111011")) InstInfo.InstKind = SRLW;
	if(BitPat(Inst, "010000??????_?????_101_?????_0110011")) InstInfo.InstKind = SRA;
	if(BitPat(Inst, "010000??????_?????_101_?????_0111011")) InstInfo.InstKind = SRAW;
	if(BitPat(Inst, "000000??????_?????_000_?????_0110011")) InstInfo.InstKind = ADD;
	if(BitPat(Inst, "000000??????_?????_000_?????_0111011")) InstInfo.InstKind = ADDW;
	if(BitPat(Inst, "010000??????_?????_000_?????_0110011")) InstInfo.InstKind = SUB;
	if(BitPat(Inst, "010000??????_?????_000_?????_0111011")) InstInfo.InstKind = SUBW;
	if(BitPat(Inst, "000000??????_?????_100_?????_0110011")) InstInfo.InstKind = XOR;
	if(BitPat(Inst, "000000??????_?????_110_?????_0110011")) InstInfo.InstKind = OR;
	if(BitPat(Inst, "000000??????_?????_111_?????_0110011")) InstInfo.InstKind = AND;
	if(BitPat(Inst, "000000??????_?????_010_?????_0110011")) InstInfo.InstKind = SLT;
	if(BitPat(Inst, "000000??????_?????_011_?????_0110011")) InstInfo.InstKind = SLTU;
	// Instruction Formats: B/S
  	if(BitPat(Inst, "???????_?????_?????_000_?????_1100011")) InstInfo.InstKind = BEQ;
  	if(BitPat(Inst, "???????_?????_?????_001_?????_1100011")) InstInfo.InstKind = BNE;
  	if(BitPat(Inst, "???????_?????_?????_100_?????_1100011")) InstInfo.InstKind = BLT;
  	if(BitPat(Inst, "???????_?????_?????_101_?????_1100011")) InstInfo.InstKind = BGE;
  	if(BitPat(Inst, "???????_?????_?????_110_?????_1100011")) InstInfo.InstKind = BLTU;
  	if(BitPat(Inst, "???????_?????_?????_111_?????_1100011")) InstInfo.InstKind = BGEU;
  	if(BitPat(Inst, "???????_?????_?????_011_?????_0100011")) InstInfo.InstKind = SD;
  	if(BitPat(Inst, "???????_?????_?????_010_?????_0100011")) InstInfo.InstKind = SW;
  	if(BitPat(Inst, "???????_?????_?????_001_?????_0100011")) InstInfo.InstKind = SH;
  	if(BitPat(Inst, "???????_?????_?????_000_?????_0100011")) InstInfo.InstKind = SB;
	// Instruction Formats: J/U
	if(BitPat(Inst, "????????????????????_?????_0110111")) InstInfo.InstKind = LUI;
	if(BitPat(Inst, "????????????????????_?????_0010111")) InstInfo.InstKind = AUIPC;
	if(BitPat(Inst, "????????????????????_?????_1101111")) InstInfo.InstKind = JAL;

    assert(InstInfo.InstKind != ILL);
    InstInfo.imm = IMM(InstInfo);
    return InstInfo;
}
